package com.onlyxiahui.app.context;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 描述：对象创建工厂 date 2016-01-6 7:45:12
 * 
 * @author XiaHui
 * @version 0.0.1
 */
public abstract class AbstractFactory extends AbstractMaterial {

	protected final Logger logger = LoggerFactory.getLogger(this.getClass());
	private Map<Class<?>, Object> map = new ConcurrentHashMap<Class<?>, Object>();
	private ReentrantLock lock = new ReentrantLock();

	public AbstractFactory(AppContext appContext) {
		super(appContext);
	}

	public abstract <T> boolean isSupport(Class<T> clazz);

	public <T> void put(Class<T> defineClass, T object) {
		map.put(defineClass, object);
	}

	/**
	 * 根据class获取对象，均为单列(有待验证😂)
	 * 
	 * @author XiaHui
	 * @param classType
	 * @return
	 */
	public <T> T getObject(Class<T> classType) {
		return getObject(classType, false, false);
	}

	/**
	 * 
	 * @author XiaHui
	 * @param classType
	 * @param createNew
	 * @return
	 */
	public <T> T getObject(Class<T> classType, boolean createNew) {
		return getObject(classType, createNew, false);
	}

	/**
	 * 
	 * @author XiaHui
	 * @param classType:被实例化的class对象
	 * @param createNew:true：不管对象是否存在，都新创建对象；false：如果对象已经存在就不新建对象
	 * @param cover:true：如果新建了对象，则覆盖原来的对象。false：不执行覆盖操作
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T getObject(Class<T> classType, boolean createNew, boolean cover) {
		final ReentrantLock lock = this.lock;
		Object object = null;
		lock.lock(); // Lock only for visibility, not mutual exclusion
		try {
			if (null != classType) {
				boolean has = map.containsKey(classType);
				object = map.get(classType);
				// TODO
				// 不知道同步对象有没有必要写，先注释吧
				if (!has || createNew) {
					object = createObject(classType);
					// 原来对象不存在，或者覆盖，并且新创建的对象不能为空
					boolean put = (!has || cover) && null != object;
					if (put) {
						map.put(classType, object);
					}
				}
			}
		} finally {
			lock.unlock();
		}
		return (T) object;
	}

	/**
	 * 根据class反射创建对象
	 * 
	 * @author XiaHui
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T createObject(Class<T> classType) {
		T object = null;

		// 判断是否可以反射实例化，排除接口、抽象类等
		boolean canInstance = isCanInstance(classType);

		if (canInstance) {
			try {
//				Class<?>[] types = { AppContext.class };
//				Constructor<?>[] cs = classType.getConstructors();

				Constructor<?> constructor = chooseConstructor(classType.getDeclaredConstructors());
				// 获取一下传入appContext对象的构造函数
//				Constructor<T> constructor = classType.getConstructor(types);
				// 如果存在传染传入appContext对象的构造函数
				if (null == constructor) {
					constructor = chooseConstructor(classType.getConstructors());
				}
				if (null != constructor) {

					Class<?>[] parameterTypes = constructor.getParameterTypes();
					Object[] argsWithDefaultValues = new Object[parameterTypes.length];
					for (int i = 0; i < parameterTypes.length; i++) {

						Class<?> argc = parameterTypes[i];

						if (null != argc) {
							if (AppContext.class.isAssignableFrom(argc)) {
								argsWithDefaultValues[i] = appContext;
							} else if (AbstractFactory.class.isAssignableFrom(argc)) {
								argsWithDefaultValues[i] = this;
							} else {
								argsWithDefaultValues[i] = map.get(argc);
							}
						}
					}
					object = (T) constructor.newInstance(argsWithDefaultValues);
				}
			} catch (Exception e) {
				logger.error("", e);
			}
			try {
				if (null == object) {
					object = classType.newInstance();
				}
			} catch (Exception e) {
				logger.error("", e);
			}
			try {
				if (object instanceof Material) {
					((Material) object).setAppContext(appContext);
				}
			} catch (Exception e) {
				logger.error("", e);
			}
		}
		return object;
	}

	public Constructor<?> chooseConstructor(Constructor<?>[] cs) {
		Constructor<?> d = null;
		List<Constructor<?>> list = new ArrayList<>();
		if (null != cs) {
			for (Constructor<?> c : cs) {
				list.add(c);
			}
		}

		list.sort((a, b) -> {

			int a1 = a.getParameterCount();
			int b1 = b.getParameterCount();

			int countA = 0;
			int countB = 0;
			if (Modifier.isPublic(a.getModifiers())) {
				countA = 1;
			}
			if (Modifier.isProtected(a.getModifiers())) {
				countA = 2;
			}
			if (Modifier.isPrivate(a.getModifiers())) {
				countA = 3;
			}

			if (Modifier.isPublic(b.getModifiers())) {
				countB = 1;
			}
			if (Modifier.isProtected(b.getModifiers())) {
				countB = 2;
			}
			if (Modifier.isPrivate(b.getModifiers())) {
				countB = 3;
			}

			if (a.getModifiers() == b.getModifiers()) {
				return a1 - b1;
			} else {
				return countA - countB;
			}
		});

		if (!list.isEmpty()) {
			for (Constructor<?> c : list) {
				if (null == d) {
					Class<?>[] css = c.getParameterTypes();
					for (Class<?> ct : css) {
						if (AppContext.class.isAssignableFrom(ct)) {
							d = c;
							break;
						}
					}
				}
			}
			if (null == d) {
				d = list.get(0);
			}
		}
		return d;
	}

	public <T> boolean isCanInstance(Class<T> classType) {
		boolean can = true;
		if (classType.isAnnotation()) {
			can = false;
		} else if (classType.isArray()) {
			can = false;
		} else if (classType.isEnum()) {
			can = false;
		} else if (classType.isInterface()) {
			can = false;
		} else if (Modifier.isAbstract(classType.getModifiers())) {
			can = false;
		}
		return can;
	}
}
